package com.java.serverpackage;


import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StreamCorruptedException;
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;

public class ClientRunnable implements Runnable{

    protected Socket clientSocket = null;
    protected ObjectInputStream input = null;
    protected ObjectOutputStream output = null;
    protected ClientRoom room;
   // protected int roomId;
    protected InetAddress clientIP;
    
        
    public ClientRunnable(Socket clientSocket) {
        //Let each ClientRunnable object keep track of which room it is part of
    	//by handing it a room reference at creation or later on using the setRoom method. 
    	//This will render a roomId unnecessary. It will also let the ClientRunnable remove 
    	//itself from the room if the client disconnects.    	
    	
    	this.clientSocket = clientSocket;
    	clientIP = clientSocket.getInetAddress();
    	
        try {
			input = new ObjectInputStream(clientSocket.getInputStream());
			output = new ObjectOutputStream(clientSocket.getOutputStream());
		} catch (StreamCorruptedException e) {
			System.out.println("StreamCorruptedException in clientRunnable constructor");
			//e.printStackTrace();
		} catch (IOException e) {
			System.out.println("IOException in clientRunnable constructor");
			//e.printStackTrace();
		}
    }

    public void run() {
    	MessagePacket msg = null;
    	ArrayList<MessagePacket> history = room.getHistory();
    	Iterator<MessagePacket> it = history.iterator();
    	try {
    		//Broadcast a message that a new player has joined
    		if(room.getMembers() > 1) 
	    		room.broadcast(new MessagePacket(1, room.getMembers()), this);
	    		
    		//Send message to yourself that you successfully joined a room
    		this.output.writeObject(new MessagePacket(3,room.getMembers()));
    		
    		//Check if there is history in the room, if so, begin by getting it
    		if(history.size() > 0)
    			this.output.writeObject(new MessagePacket(4,history));
    		    		
    		//Broadcast data messages to all players in a room
            while(!clientSocket.isClosed()) {  
            	if(!clientSocket.isClosed()) {
	            	msg = (MessagePacket) input.readObject();
	            	System.out.println(clientSocket.getInetAddress().toString() + " : " + msg.getMsg() + " : " + room.getId());
	            	// If it is a normal data message
	            	if(msg.getId() == 0)
	            		room.addHistory(msg);
	            	// If it is an undo message remove the message from the history before broadcasting
	            	if(msg.getId() == 5) {
	            		while(it.hasNext()) {
	            			MessagePacket message = it.next();
	            			if(message.getPid() == msg.getPid()) {
	            				history.remove(message);
	            			}
	            			
	            		}
	            	}
	            	room.broadcast(msg,this);	
            	}
			}       
        // if the client disconnects, close the input stream, the socket and remove it from the room
        } catch (IOException e) {
        	System.out.println("IOException in clientRunnable");
        } catch (ClassNotFoundException e) {
        	System.out.println("ClassNotFoundException in clientRunnable");
		} finally {      
			
            //Remove client from room and make him able to connect to server again
			room.removeClient(this);
			MultiThreadedServer.connectedClients.put(clientIP, false);
			
			//Broadcast message to tell a member has disconnected
			if(room.getMembers() > 0) 
	    		room.broadcast(new MessagePacket(2, room.getMembers()), this);				
			
			try {
				output.close();
            	input.close();
                clientSocket.close(); 	
			}catch (IOException e) { }//e.printStackTrace(); 	
			
			 System.out.println("Client has disconnected");	
			
			//If there are no players in room, delete room from list
            if(room.getMembers() == 0){
            	MultiThreadedServer.clientRoomList.remove(room); 
            	MultiThreadedServer.decrementRoomID();
            }
		}
    	return;
      
    }
    
    public ObjectOutputStream getOutputStream() {
    	return output;
    }
    
    public void setRoom(ClientRoom room) {
    	this.room = room;
    }
}

